//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
#include <elasys_server.h>
#include <stdlib.h>
#include "shareMemTypes.h"

////////////////////////////////////////////////////////
//Open share memery
////////////////////////////////////////////////////////

static list s_OpenShareMemList = LIST_INIT(s_OpenShareMemList);
static RTL_CRITICAL_SECTION s_OpenShareMemCritcalSection;
static BOOL bOpenInitFlag = TRUE;

#define IdSize 4

#define GetShareMemId(id, wszid) do{ \
                                memset(wszid, 0, IdSize*sizeof(wchar_t));\
                                _ltow((long)id, wszid, 16); \
                                }while(0)

EXTERN_C ECode __cdecl OpenShareMem(
    ULONG uIdAndOffset,
    Address *pShareAddr)
{
    wchar_t wszId[IdSize];
    ShareMemData *pShareMemNode = NULL;
    ULONG id = uIdAndOffset & 0xFFF;
    ULONG offset = uIdAndOffset >> 12;

    if (NULL == pShareAddr) {
        return E_INVALID_ARGUMENT;
    }

    pShareMemNode = (ShareMemData *)malloc(sizeof(ShareMemData));
    if (NULL == pShareMemNode) {
        return E_OUT_OF_MEMORY;
    }

    GetShareMemId(id, wszId);
    pShareMemNode->hFileMap = OpenFileMappingW(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, wszId);
    if (NULL == pShareMemNode->hFileMap) {
        free(pShareMemNode);
        return E_INVALID_ARGUMENT;
    }

    pShareMemNode->ShareAddr = (Address)MapViewOfFile(pShareMemNode->hFileMap,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);

    if (NULL != pShareAddr) {
        *pShareAddr = pShareMemNode->ShareAddr + offset;
        pShareMemNode->offset = offset;
    }

    if (bOpenInitFlag) {//First time enter this function. Take a new function to init this object may be better.
        InitializeCriticalSection(&s_OpenShareMemCritcalSection);
        bOpenInitFlag = FALSE;
    }

    EnterCriticalSection(&s_OpenShareMemCritcalSection);
    list_add_head( &s_OpenShareMemList, &pShareMemNode->Entry );
    LeaveCriticalSection(&s_OpenShareMemCritcalSection);

    return NOERROR;
}

EXTERN_C ECode __cdecl FreeOpenShareMem(
    Address ShareAddr)
{
    list *ptr;

    if (0 == ShareAddr) {
        return NOERROR;
    }

    LIST_FOR_EACH(ptr, &s_OpenShareMemList)
    {
        ShareMemData *pShareMemNode = LIST_ENTRY( ptr, struct ShareMemData, Entry );
        if (pShareMemNode->ShareAddr + pShareMemNode->offset == ShareAddr) {
            UnmapViewOfFile((void *)ShareAddr);
            CloseHandle(pShareMemNode->hFileMap);
            EnterCriticalSection(&s_OpenShareMemCritcalSection);
            list_remove( &pShareMemNode->Entry );
            free(pShareMemNode);
            LeaveCriticalSection(&s_OpenShareMemCritcalSection);
            return NOERROR;
        }
    }

    return E_INVALID_ARGUMENT;
}

////////////////////////////////////////////////////////
//Alloc share memery
////////////////////////////////////////////////////////
static list s_AllocShareMemList = LIST_INIT(s_AllocShareMemList);
#define GenerateShareMemId(id, wszid) do{ \
                                id++; \
                                memset(wszid, 0, IdSize*sizeof(wchar_t));\
                                _ltow((long)id, wszid, 16); \
                            }while(0)

static RTL_CRITICAL_SECTION s_AllocShareMemCritcalSection;
static BOOL bAllocInitFlag = TRUE;

EXTERN_C ECode AllocShareMem(
    MemorySize size,
    MemoryProtection protection,
    Address *pShareAddr,
    ULONG *pId)
{
    static ULONG GenID = 0;
    ULONG sign = GenID;
    wchar_t wszId[IdSize];
    ShareMemData *pShareMemNode = NULL;

    if (0 == size) {
        return NOERROR;
    }

    pShareMemNode = (ShareMemData *)malloc(sizeof(ShareMemData));
    if (NULL == pShareMemNode) {
        return E_OUT_OF_MEMORY;
    }

    do {
        GenerateShareMemId(GenID, wszId);
        if(GenID >= 0xFFF){
            GenID = 0;
        }
        pShareMemNode->hFileMap = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,0, (DWORD)size, wszId);
        if (NULL == pShareMemNode->hFileMap) {
            goto ERR;
        }
        else if (GetLastError() == ERROR_ALREADY_EXISTS) {//todo: Wish really work when in differnce processes
            CloseHandle(pShareMemNode->hFileMap);
            if (sign == GenID) {
                goto ERR;
            }
        }
        else {
            break;
        }
    } while (1);

    pShareMemNode->ShareAddr = (Address)MapViewOfFile(pShareMemNode->hFileMap,FILE_MAP_READ|FILE_MAP_WRITE,0,0,0);
    pShareMemNode->uInfo.id= GenID;

    if ((NULL != pId)) {
        *pId = GenID;
    }

    if (NULL != pShareAddr) {
        *pShareAddr = pShareMemNode->ShareAddr;
    }

    if (bAllocInitFlag) {//First time enter this function. Take a new function to init this object may be better.
        InitializeCriticalSection(&s_AllocShareMemCritcalSection);
        bAllocInitFlag = FALSE;
    }
    EnterCriticalSection(&s_AllocShareMemCritcalSection);
    list_add_head( &s_AllocShareMemList, &pShareMemNode->Entry );
    LeaveCriticalSection(&s_AllocShareMemCritcalSection);
    return NOERROR;

ERR:
    if (NULL != pShareMemNode) {
        free(pShareMemNode);
    }
    return E_OUT_OF_MEMORY;
}

EXTERN_C ECode FreeAllocShareMem(
    Address ShareAddr)
{
    list *ptr;

    if (0 == ShareAddr) {
        return NOERROR;
    }

    LIST_FOR_EACH(ptr, &s_AllocShareMemList)
    {
        ShareMemData *pShareMemNode = LIST_ENTRY( ptr, struct ShareMemData, Entry );
        if (pShareMemNode->ShareAddr == ShareAddr) {
            UnmapViewOfFile((void *)ShareAddr);
            CloseHandle(pShareMemNode->hFileMap);
            EnterCriticalSection(&s_AllocShareMemCritcalSection);
            list_remove( &pShareMemNode->Entry );
            free(pShareMemNode);
            LeaveCriticalSection(&s_AllocShareMemCritcalSection);
            return NOERROR;
        }
    }

    return E_INVALID_ARGUMENT;
}

/*
EXTERN_C ECode ConvertAllocAddrToId(
    Address ShareAddr,
    ULONG offset, ULONG *pId)
{
    list *ptr;

    if (0 == ShareAddr) {
        return NOERROR;
    }

    LIST_FOR_EACH(ptr, &s_AllocShareMemList)
    {
        ShareMemData *pShareMemNode = LIST_ENTRY( ptr, struct ShareMemData, Entry );
        if (pShareMemNode->ShareAddr == ShareAddr) {
            *pId = pShareMemNode->uInfo.id + (offset << 12);
            return NOERROR;
        }
    }

    return E_INVALID_ARGUMENT;
}
*/

// hack for Sandalwood project.

EXTERN_C ECode ConvertAllocAddrToId(
    Address ShareAddr,
    ULONG offset, ULONG *pId)
{
    if (0 == ShareAddr) {
        return NOERROR;
    }
    *pId = (ULONG)ShareAddr;

    return NOERROR;
}


EXTERN_C BOOL  __stdcall DllMain(HANDLE hModule, DWORD dwReason, PVoid lpReserved)
{
    BOOL bRes = TRUE;
    switch (dwReason) {
        case DLL_PROCESS_ATTACH:
            break;
        case DLL_PROCESS_DETACH:
            break;
    }
    return bRes;
}
